#include <boost/thread/thread.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/network/include/http/server.hpp>
#include <boost/network/utils/thread_pool.hpp>
#include <boost/range/algorithm/find_if.hpp>

#include <iostream>

using namespace std;

namespace net = boost::network;
namespace http = boost::network::http;
namespace utils = boost::network::utils;

struct async_hello_world;
typedef http::async_server<async_hello_world> server;

typedef map<string, string> key_value;

/*void parse_url(string &url, string &path, string &para)
{
	size_t path_start = url.find('/');
	size_t para_start = url.find('?');
	if (path_start == string::npos) return;
	if (para_start == string::npos)
	{
		path = url.substr(path_start + 1);
		return;
	}
	else
	{		
		path = url.substr(path_start + 1, para_start - path_start - 1);
		para = url.substr(para_start + 1);
	}
}*/
void parse_para(string para, map<string, string> &pair)
{
	size_t equal, an;
	equal = para.find('=');
	an = para.find('&');
				
	while (equal != string::npos)
	{
		string left, right;
		left = para.substr(0, equal);
		if (an == string::npos)
		{
			right = para.substr(equal + 1);
			para = "";
		}
		else
		{
			right = para.substr(equal + 1, an - equal - 1);
			para = para.substr(an + 1);
		}
		if (left == "")
		{
			cout << "incorrect get parameter format" << endl;
			return;
		}
		string key, value;

		net::uri::decode(left, std::back_inserter(key));
		net::uri::decode(right, std::back_inserter(value));

		cout << key << "=" << value << endl; 
		pair[key] = value;
		equal = para.find('=');
		an = para.find('&');
	}
}


void
extern_get_users(server::connection_ptr &connection);
void
extern_kick_users(server::connection_ptr &connection, key_value &para);
void
extern_send_message(server::connection_ptr &connection, key_value &para);
void
extern_set_rate(server::connection_ptr &connection, key_value &para);

struct async_hello_world {
	void get_post(server::connection_ptr conn, server::request const &req) {
		int cl;
		server::request::headers_container_type const &hs = req.headers;
		for(server::request::headers_container_type::const_iterator it = hs.begin(); it!=hs.end(); ++it) {
			if(boost::to_lower_copy(it->name)=="content-length") {
				cl = boost::lexical_cast<int>(it->value);
				break;
			}
		}
		read_chunk(cl, conn, req);
	}

	void read_chunk(size_t left2read, server::connection_ptr conn, server::request const &req, string body = "") {
		std::cout << "left2read: " << left2read << std::endl;
		conn->read(
			boost::bind(
				&async_hello_world::handle_post_read,
				this,
				_1, _2, _3, conn, req, left2read, body
				)
			);
	}

	void handle_post_read(
		server::connection::input_range range, boost::system::error_code error, size_t size, 
		server::connection_ptr conn, server::request const &req, size_t left2read, string body) {
		if(!error) {
			std::cout << "read size: " << size << std::endl;
			body.append(boost::begin(range), size);
			size_t left = left2read - size;
			if(left>0) {
				read_chunk(left, conn, req, body);
			} else {
				connection_write(req, conn, body);
			}
				
		}
		else
			std::cout << "error: " << error.message() << std::endl;
	}


    struct is_content_length {
        template <class Header>
        bool operator()(Header const & header) {
            return boost::iequals(name(header), "content-length");
        }
    };

    void operator()(server::request const & request, server::connection_ptr connection) {		
		string squery = request.method;

		if (squery.find("POST") != string::npos)
			get_post(connection, request);
		else if (squery.find("GET") != string::npos)
		{
			net::uri::uri instance("http://" + request.source + request.destination);
			string query_body = net::uri::query(instance);
			connection_write(request, connection, query_body);
		}
		else
			return;

    }
	void connection_write(server::request const & request, server::connection_ptr connection, string body)
	{
		net::uri::uri instance("http://" + request.source + request.destination);
		string surl = net::uri::decoded_path(instance);

		key_value queries;
		parse_para(body, queries);

		cout << request.source + request.destination << " method: " << request.method << endl;
		cout << "body:" << body << endl;

		if (surl.find("get_users") != string::npos)
			extern_get_users(connection);
		else if (surl.find("kick_user") != string::npos)
			extern_kick_users(connection, queries);
		else if (surl.find("send_message") != string::npos)
			extern_send_message(connection, queries);
		else if (surl.find("set_rate") != string::npos)
			extern_set_rate(connection, queries);
		else 
			connection->write("http server cpp-netlib/0.9");
	}	
};


int http_start() {

	utils::thread_pool *thread_pool = new utils::thread_pool(); 
	async_hello_world *handler = new async_hello_world();
	std::string port = "23456";
	server *instance = new server("0.0.0.0", port, *handler, *thread_pool, http::_reuse_address=true);
	new boost::thread(boost::bind(&server::run, instance));

    return 0;
}
